package com.cino.interceptor;

import com.baomidou.mybatisplus.annotation.TableName;
import com.cino.util.ThreadLocalUtil;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Properties;

/**
 * UPDATE操作拦截器
 * 用于拦截UPDATE操作，比对对象变更并记录审计日志
 */
@Intercepts({
    @Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class}
    )
})
public class UpdateInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 获取MyBatis执行参数
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        
        // 2. 只拦截UPDATE操作，但排除审计日志表，并且只有被@AuditLog注解的方法才需要
        if (ms.getSqlCommandType() == SqlCommandType.UPDATE && !isAuditLogTable(ms) && ThreadLocalUtil.shouldCaptureId()) {
            // 3. 获取表名
            String tableName = getTableNameFromMappedStatement(ms);
            if (tableName != null) {
                ThreadLocalUtil.setObjectType(tableName);
            }
            
            // 4. 提取objectId并存储到ThreadLocal
            String objectId = extractId(parameter);
            if (objectId != null) {
                ThreadLocalUtil.setId(objectId);
            }
            
            // 5. 执行原UPDATE操作
            return invocation.proceed();
        }
        
        // 7. 执行原UPDATE操作
        return invocation.proceed();
    }
    
    /**
     * 从参数对象中提取ID
     * 
     * 支持的对象类型：
     * 1. 基本类型ID（Long、Integer、String等）
     * 2. 有getId()方法的实体对象
     * 3. Map类型的参数（递归查找）
     */
    private String extractId(Object parameter) {
        if (parameter == null) {
            return null;
        }
        
        // 情况1：参数是基本类型ID（Long、Integer、String等）
        if (parameter instanceof Long || parameter instanceof Integer || 
            parameter instanceof String || parameter instanceof Number) {
            return parameter.toString();
        }
        
        // 情况2：参数是实体对象，尝试调用getId()方法
        try {
            Method getId = parameter.getClass().getMethod("getId");
            Object idValue = getId.invoke(parameter);
            return idValue != null ? idValue.toString() : null;
        } catch (Exception e) {
            // 情况3：参数是Map类型，递归查找
            if (parameter instanceof Map) {
                Map<?, ?> paramMap = (Map<?, ?>) parameter;
                for (Object value : paramMap.values()) {
                    String id = extractId(value);
                    if (id != null) return id;
                }
            }
        }
        return null;
    }
    
    /**
     * 从MappedStatement获取表名
     */
    private String getTableNameFromMappedStatement(MappedStatement ms) {
        try {
            // 1. 获取Mapper类名
            String statementId = ms.getId();
            String mapperClassName = statementId.substring(0, statementId.lastIndexOf('.'));
            Class<?> mapperClass = Class.forName(mapperClassName);
            
            // 2. 获取实体类
            Class<?> entityClass = getEntityClassFromMapper(mapperClass);
            if (entityClass != null) {
                // 3. 获取表名
                return getTableNameFromEntity(entityClass);
            }
        } catch (Exception e) {
            System.err.println("获取表名失败: " + e.getMessage());
        }
        return null;
    }
    
    /**
     * 从Mapper类获取实体类
     */
    private Class<?> getEntityClassFromMapper(Class<?> mapperClass) {
        try {
            // 获取Mapper的泛型参数
            Type[] genericInterfaces = mapperClass.getGenericInterfaces();
            for (Type genericInterface : genericInterfaces) {
                if (genericInterface instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    if (actualTypeArguments.length > 0) {
                        return (Class<?>) actualTypeArguments[0];
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("获取实体类失败: " + e.getMessage());
        }
        return null;
    }
    
    /**
     * 从实体类获取表名
     */
    private String getTableNameFromEntity(Class<?> entityClass) {
        try {
            TableName tableNameAnnotation = entityClass.getAnnotation(TableName.class);
            if (tableNameAnnotation != null) {
                return tableNameAnnotation.value();
            }
            //TODO替换BusinessException
            throw new RuntimeException("实体类 " + entityClass.getName() + " 缺少@TableName注解");
        } catch (Exception e) {
            System.err.println("获取@TableName注解失败: " + e.getMessage());
            return null;
        }
    }
    
    /**
     * 判断是否是审计日志表的操作
     */
    private boolean isAuditLogTable(MappedStatement ms) {
        String statementId = ms.getId();
        return statementId.contains("AuditLog") || statementId.contains("audit_log");
    }
    
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 无需设置额外属性
    }
}
